using System;
using System.Collections.Generic;
using System.Text;
using JobShopModel.Model.FactoryLvl;
using JobShopModel.Model.Station;
using JobShopModel.Model.JobLvl;
using JobShopModel.Model;

namespace JobShopModel.Events
{
    /// <summary>
    /// Last event of the simulation for a particular job.
    /// Terminate the corresponding Job and compute the last statistics.
    /// </summary>
    /// <seealso cref="StationIO"/>
    /// <seealso cref="Job"/>
    public class FinishOperation : StationEvent
    {
        /// <summary>
        /// Job related to this Event
        /// </summary>
        private Job currentJob;

        /// <summary>
        /// Default Constructor:
        /// Set all the element from the base class
        /// Store the job corresponding to the event
        /// <seealso cref="Operation"/>
        /// </summary>
        /// <param name="sTime">Time the event is called</param>
        /// <param name="operatingStation">Station related to the Event</param>
        /// <param name="currentJob">Job containing the Operation to be done</param>
        public FinishOperation(Time sTime, StationsName operatingStation, Job currentJob)
            : base(sTime, operatingStation)
        {
            this.currentJob = currentJob;
        }

        /// <summary>
        /// Polymorphic method implemented by all event types
        /// </summary>
        /// <exception cref="Exception"> When the Job contains operation that have not been finished yet</exception>
        public override void Process()
        {
            Time elapsedTime = new Time();
            Factory fact = Factory.getInstance();

            if (StationName != StationsName.IOSTATION)
            {
                RegularStation currentStation = (RegularStation)OperatingStation;
                Operation currentOperation = currentJob.CurrentOperation;

                //Update logs
                Logger.InsertElement(new LogItem(ItemPriorities.NORMAL,fact.CurrentTime,StationName+" has finished: "+currentJob.ToString()));

                //Get the machine that hold the operation
                Machine operatingMachine = currentStation.getMachine(currentOperation);
                
                //Set the machine to blocked, it store the time.
                operatingMachine.SetBlocked(fact.CurrentTime);
                
                //Update the states
                currentStation.TotalOperationTime = currentOperation.ServiceTime;
                currentStation.AvgOperatingTime = currentStation.TotalOperationTime / fact.CurrentTime;
                //Set the operation to Blocked
                currentOperation.setBlocked(fact.CurrentTime);
            
                //Set the Job into the fifo list of the transporter
                fact.Transporter.AddJob(currentJob);
                //Store the time it started to be in the Queue
                currentJob.StartWaitingFIFO(fact.CurrentTime);

                Logger.InsertElement(new LogItem(ItemPriorities.SLOW, fact.CurrentTime, currentStation + ", Adding the job to the Transporter Queue"));

                //Call a transport in the 
                RequestTransport nextEvent = new RequestTransport(fact.CurrentTime);
                fact.EventQueue.insertEvent(nextEvent);
                Logger.InsertElement(new LogItem(ItemPriorities.SLOW, fact.CurrentTime, StationName + " is requesting the transporter"));
                Logger.InsertElement(new LogItem(ItemPriorities.CREATION, fact.CurrentTime, "Station: " + StationName + " has inserted: " + nextEvent));
            }
            else
            {
                throw new Exception("Error: This event should not be used when there is only one operation left");
            }
        }

        /// <summary>
        /// Comparison operator (for IComparable)
        /// </summary>
        /// <param name="event1">First event</param>
        /// <param name="event2">Second event</param>
        /// <returns>Returns -1 if event1 occurs first, 1 if event2 occurs first, 0 if the times are the same</returns>
        public override int CompareTo(IEvent event1, IEvent event2)
        {
            if (event1.ExecTime < event2.ExecTime)
                return -1;
            else if (event1.ExecTime > event2.ExecTime)
                return 1;
            else
                return 0;
        }

        public override string ToString()
        {
            return "Finish_Operation(" + ExecTime + ")";
        }
    }
}
